package com.br.aiops.schedule;

import com.br.aiops.alert.AlertSender;
import com.br.aiops.counter.AlertLogCounter;
import com.br.aiops.counter.Counter;
import com.br.aiops.counter.CounterLadderValue;
import com.br.aiops.listeners.ladder.LadderCycleStatus;
import com.br.aiops.listeners.ladder.LadderCycleStatusCache;
import com.br.aiops.rule.Rule;
import com.br.aiops.rule.RuleCache;
import com.br.aiops.schedule.judge.ResumeJudges;
import com.br.aiops.storage.PersistentCounterService;
import com.br.aiops.utils.Constants;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Component
public abstract class AbstractSchedule {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSchedule.class);

    public static final int ALERT_TYPE_ALERT = 1;

    /**
     * 每个AlertLogCounter的发送状态，1代表已经在周期内发送过；0代表未发送
     */
    protected Map<String, Status> prevCountStatus = new ConcurrentHashMap<>();
    protected Map<String, Status> currentCountStatus = new ConcurrentHashMap<>();

    protected ScheduleCycle scheduleCycle;

    private final Object mutex = new Object();

    @Autowired
    private RuleCache ruleCache;

    @Autowired
    private Counter counter;

    @Autowired
    private AlertSender alertSender;

    @Autowired
    private PersistentCounterService persistentCounterService;

    /**
     * 执行调度方法
     */
    protected abstract void run();

    protected void runTask() {
        //初始化周期
        initCycle();
        //清理过期状态，并移动current到prev
        clear();
        LOGGER.info("调度器：{}，移动后两周期统计数，currentCountStatus: {}, prevCountStatus: {}", getClass().getName(), currentCountStatus, prevCountStatus);

        delay();
        //执行恢复过程
        List<AlertLogCounter> counts = getCounts();
        counts.forEach(this::releaseIfResume);
    }

    /**
     * 延迟10s，方便恢复统计全面
     */
    protected void delay() {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        try {
            lock.lock();
            condition.await(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            LOGGER.error("sleep延迟出现错误。", e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 从counter对象获取该duration的rule，根据rule从counter对象获取对应rules的counter
     * @return
     */
    protected abstract List<AlertLogCounter> getCounts();

    /**
     * 初始化调度周期；在整点初始化
     */
    protected abstract void initCycle();
    /**
     * 根据日志获取对应的时间区间统计数据
     * @param alertLogCounter
     * @return 对应区间的统计数据
     */
    public Map<String, Status> getCountStatus(AlertLogCounter alertLogCounter) {
        if (getCycle().inCurrentCycle(alertLogCounter.getAlertLog().getLogTime())) {
            return currentCountStatus;
        } else if (getCycle().inPrevCycle(alertLogCounter.getAlertLog().getLogTime())) {
            return prevCountStatus;
        }
        LOGGER.warn("alertLogCounter: {} 未找到对应的区间，scheduleCycle: {}，logTime: {}.", alertLogCounter.counterKey(),
                scheduleCycle, alertLogCounter.getAlertLog().getLogTime());
        return null;
    }

    /**
     * 清除过期数据（只保留上一个周期）
     */
    protected synchronized void clear() {
        prevCountStatus.clear();
        prevCountStatus.putAll(currentCountStatus);
        currentCountStatus.clear();
    }

    /**
     * 标识对应的区间已发送（超过上一周期区间的不处理）
     * @param alertLogCounter
     */
    public void flag(AlertLogCounter alertLogCounter) {
        Map<String, Status> countStatus = getCountStatus(alertLogCounter);
        if (Objects.nonNull(countStatus)) {
            countStatus.computeIfPresent(alertLogCounter.counterKey(), (k, v) -> {
                v.setFlag(true);
                return v;
            });
        }
    }

    /**
     * 对应的区间是否已经标识过（标识过代表发送过）
     * @param alertLogCounter
     * @return
     */
    public boolean isFlag(AlertLogCounter alertLogCounter) {
        Map<String, Status> countStatus = getCountStatus(alertLogCounter);
        if (Objects.nonNull(countStatus)) {
            return countStatus.containsKey(alertLogCounter.counterKey())
                    && countStatus.get(alertLogCounter.counterKey()).isFlag();
        }
        return false;
    }

    /**
     * 获取对应时区的统计个数
     * @param alertLogCounter
     * @return
     */
    public long getCount(AlertLogCounter alertLogCounter) {
        Map<String, Status> countStatus = getCountStatus(alertLogCounter);
        if (Objects.nonNull(countStatus)) {
            return countStatus.getOrDefault(alertLogCounter.counterKey(), new Status()).getCount();
        }
        return 0;
    }

    /**
     * 对映射的区间进行统计
     * @param alertLogCounter
     */
    public void count(AlertLogCounter alertLogCounter) {
        Map<String, Status> countStatus = getCountStatus(alertLogCounter);
        if (Objects.nonNull(countStatus)) {
            //没有则新增，有则累加
            countStatus.compute(alertLogCounter.counterKey(), (k, v) -> {
                if (v == null) {
                    v = new Status();
                    v.setCount(1L);
                } else {
                    v.setCount(v.getCount() + 1);
                }
                return v;
            });
        }
    }

    /**
     * 如果告警恢复则停止阶梯告警调度器（如果有），并且发送恢复告警，同时清空统计数据
     * @param alertLogCounter
     */
    public void releaseIfResume(AlertLogCounter alertLogCounter) {
        LOGGER.info("调度器：{} 判断该告警：{} 的状态,currentCountStatus: {}，prevCountStatus: {}", getClass().getName(), alertLogCounter.counterKey(),
                currentCountStatus, prevCountStatus);
        //删除阶梯
        if (isResume(alertLogCounter)) {
            //在创建周期之前的counter恢复时不发送告警（阶梯中没有统计并告警）
            if (shouldAlert(alertLogCounter)) {
                //发送恢复告警，如果是通知则不发送
                if (alertLogCounter.getRule().getAlertType() == ALERT_TYPE_ALERT) {
                    LOGGER.info("调度器：{} 发送恢复告警。", getClass().getName());
                    alertSender.sendResumeMessage(alertLogCounter);
                }
            }

            String counterKey = alertLogCounter.counterKey();

            //次数阶梯没有创建周期
            if (alertLogCounter.shouldLadderAlert()) {
                if (alertLogCounter.getRule().getLadderType() != Constants.LADDER_TYPE_TIMES) {
                    LOGGER.info("调度器：{} 告警恢复，即将删除周期阶梯：{}", getClass().getName(), LadderCycleStatusCache.getLadderCycleStatus().remove(counterKey));
                }

                LOGGER.info("调度器：{} 删除阶梯Value，被删除的CounterKey：{}", getClass().getName(), counterKey);
                CounterLadderValue.remove(counterKey);
            }

            //标记为恢复
            try {
                persistentCounterService.resume(counterKey);
            } catch (Exception e) {
                LOGGER.error("更新数据中counter key为恢复状态失败。", e);
            }


            //清空AlertLogCounter
            LOGGER.info("调度器：{} 删除计数器Counter：{}", getClass().getName(), counterKey);
            counter.getCounter().remove(counterKey);
        }
    }

    /**
     * 判断如果在创建阶梯之前的日志，则不发送告警
     * @param alertLogCounter
     * @return
     */
    private boolean shouldAlert(AlertLogCounter alertLogCounter) {
        if (alertLogCounter.shouldLadderAlert()) {
            if (alertLogCounter.getRule().getLadderType() != Constants.LADDER_TYPE_TIMES) {
                LadderCycleStatus cycleStatus = LadderCycleStatusCache.getLadderCycleStatus().get(alertLogCounter.counterKey());
                return !cycleStatus.beforeFirstCycle(alertLogCounter.getAlertLog().getLogTime());
            }
        }
        return true;
    }

    /**
     * 是否恢复
     * @param alertLogCounter
     * @return
     */
    private boolean isResume(AlertLogCounter alertLogCounter) {
        return ResumeJudges.isResume(this, alertLogCounter);
    }

    /**
     * 获取该duration内告警或恢复的Counter；
     * 该方法不但返回告警判断类型包含duration的counter，还包含告警判断类型为alertTimes的counter（主要用来恢复）
     * @param duration 需要传入持续时长
     * @return
     */
    protected List<AlertLogCounter> getAlertLogCounters(Integer duration) {
        //清除那些因为禁用规则而遗留在内存的counter数据
        clearDisableCounters();

        List<Long> ruleIds = ruleCache.getRules().stream()
                .filter(rule -> Objects.equals(rule.getDuration(), duration))
                .map(Rule::getId).collect(Collectors.toList());

        return counter.getCounter()
                .values()
                .stream()
                .filter(alertLogCounter -> ruleIds.contains(alertLogCounter.getRule().getId()))
                .collect(Collectors.toList());
    }

    private void clearDisableCounters() {
        List<Long> allRuleIds = ruleCache.getRules().stream()
                .map(Rule::getId).collect(Collectors.toList());
        List<String> disables = counter.getCounter()
                .values()
                .stream()
                .filter(alertLogCounter -> !allRuleIds.contains(alertLogCounter.getRule().getId()))
                .map(AlertLogCounter::counterKey)
                .collect(Collectors.toList());
        disables.forEach(disable -> counter.getCounter().remove(disable));
    }

    public ScheduleCycle getCycle() {
        //调度还没有执行时，该值为空
        if (Objects.isNull(scheduleCycle)) {
            synchronized (mutex) {
                if (Objects.isNull(scheduleCycle)) {
                    initCycle();
                }
            }
        }
        return scheduleCycle;
    }

    public Map<String, Status> getCurrentCountStatus() {
        return currentCountStatus;
    }

    public Map<String, Status> getPrevCountStatus() {
        return prevCountStatus;
    }

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Status {
        private volatile long count;
        private volatile boolean flag;
    }
}
